#include <webx/route.h>
#include <http/HttpHelper.h>

class TraceModule : public webx::ProcessBase
{
protected:
	int process();
};

HTTP_WEBCGI(CGI_PRIVATE, "${filename}")
DEFINE_HTTP_CGI_EXPORT_FUNC(TraceModule)

static HttpServer* app = HttpServer::Instance();

class LoggerWorkItem : public WorkItem
{
	int idx = 0;
	HostItem loghost;
	LogThread logfile;
	mutable Mutex mtx;
	queue<string> logqueue;
	multimap<string, string> logmap;

public:
	void run()
	{
		if (idx % 10 == 0) tryUpload();

		trySync();

		idx++;
	}
	bool init()
	{
		string path = LogThread::Instance()->getLogPath();
		int maxsz = LogThread::Instance()->getMaxFileSize();

		yaml::config("log.port", loghost.port);
		yaml::config("log.host", loghost.host);

		return logfile.init(path + "/webapp", maxsz, true);
	}
	HostItem getHost() const
	{
		Locker lk(mtx);

		return loghost;
	}
	bool setHost(const string& host, int port)
	{
		Locker lk(mtx);

		if (loghost.canUse()) return false;

		loghost.host = host;
		loghost.port = port;

		return true;
	}

	bool tryUpload()
	{
		string link;
		JsonElement json;

		CHECK_FALSE_RETURN(loghost.canUse());

		{
			Locker lk(mtx);

			CHECK_FALSE_RETURN(unsafePop(json) > 0);

			link = HttpHelper::GetLink("tracemodule", loghost.host, loghost.port);
		}

		SmartBuffer data = HttpHelper::GetResult(link, json.toString());

		if (data.isNull() && HttpHelper::GetLastStatus() == XG_SENDFAIL)
		{
			data = HttpHelper::GetResult(link, json.toString());
		}

		return data.isNull() ? false : true;
	}
	bool push(const string& msg)
	{
		Locker lk(mtx);

		if (logqueue.size() > 1000000) return false;

		logqueue.push(msg);

		return true;
	}
	int unsafePop(JsonElement& json)
	{
		if (logqueue.empty()) return 0;

		int len = 0;
		int idx = 0;
		JsonElement list = json.addArray("list");

		while (logqueue.size() > 0 && len < 8 * 1024 * 1024)
		{
			const string& msg = logqueue.front();
			len += msg.length();
			list[idx++] = msg;
			logqueue.pop();
		}

		return idx;
	}

	string pop()
	{
		string res;
		Locker lk(mtx);

		if (logmap.empty()) return res;

		auto it = logmap.begin();

		while (it != logmap.end() && res.length() < 8 * 1024 * 1024)
		{
			res += it->second;
			it = logmap.erase(it);
		}

		return std::move(res);
	}
	bool trySync()
	{
		string msg = pop();

		if (msg.empty()) return false;

		return logfile.write(msg.c_str(), msg.length());
	}
	bool save(const string& msg)
	{
		size_t pos = msg.find("]");

		if (pos < 18 || pos == string::npos) return false;

		for (int i = 0; i < 5; i++)
		{
			mtx.lock();

			if (logmap.size() > 1000000)
			{
				mtx.unlock();

				Sleep(100);

				continue;
			}

			logmap.insert(pair<string, string>(msg.substr(1, pos), msg));

			mtx.unlock();

			return true;
		}

		return false;
	}

	static const sp<LoggerWorkItem>& Instance()
	{
		static sp<LoggerWorkItem> logger = newsp<LoggerWorkItem>();

		return logger;
	}
};

static int GetLogHost(char* host, int* port)
{
	HostItem item = LoggerWorkItem::Instance()->getHost();

	if (item.host.empty()) return XG_NOTFOUND;

	strcpy(host, item.host.c_str());
	*port = item.port;

	return XG_OK;
}

static int SetLogHost(const char* host, int port)
{
	return LoggerWorkItem::Instance()->setHost(host, port) ? XG_OK : XG_FAIL;
}

static void LogCallback(const char* msg, int len)
{
	LoggerWorkItem::Instance()->push(string(msg, msg + len));
}

EXTERN_DLL_FUNC int HttpPluginInit(IHttpServer* app)
{
	Process::SetObject("HTTP_SET_LOG_HOST_FUNC", (void*)SetLogHost);
	Process::SetObject("HTTP_GET_LOG_HOST_FUNC", (void*)GetLogHost);

	HttpServer::Instance()->setLogCallback(LogCallback);
	
	if (LoggerWorkItem::Instance()->init())
	{
		stdx::timer(100, LoggerWorkItem::Instance());
	}

	return XG_OK;
}

int TraceModule::process()
{
	JsonElement data(request->getDataString());

	if (data.isObject())
	{
		JsonElement list = data.get("list");

		for (JsonElement item : list)
		{
			LoggerWorkItem::Instance()->save(item.asString());
		}
	}

	json["code"] = XG_OK;

	return XG_OK;
}